#ifndef HASHFIRSTOPERANDSUBTRACTIONRESULTBOX_H_JM_20130206
#define HASHFIRSTOPERANDSUBTRACTIONRESULTBOX_H_JM_20130206
/// @file HashFirstOperandSubtractionResultBox.h
/// Box realizujjuci post-processing hashovacej tabulky pre aktivity, ktore 
/// nedokazu same pocas vypoctu urcit korektny vysledok.

#include "../../RootBox.h"
#include "../../DbOperationVersion.h"

namespace jm4bbx {
	
	/**
	 * Box detekuje zaznam v hashmape, ktore maju nenulovy vyskyt. 
	 * Tieto zaznamy posle dalej ako vysledok (vkalda se len 1 index a to ako 
	 * prvy zdvojice vyslednych indexov - druhy je nedefinovany).
	 * 
	 * Vyuzitie ma box ako post-processing hashmapy, nakolko niektore aktivity
	 * sice vykonavaju logiku operacie, ale az kym neskonci posledny z tychto 
	 * boxov tak nie je jasne co je vysledok.
	 * 
	 * Samotny box dostane infoemacie v definicii modelu, ktrou cast hashmapy 
	 * ma spracovat. {@link #PARALLELISM_FACTOR_PARAM_NAME} urcuje stupen 
	 * paralelneho spracovania  a {@link PARALLELISM_INDEX_PARAM_NAME} urcuje 
	 * usek, ktory ma spracovat aktualny box.
	 * 
	 * @tparam HashBoxT box, ktory zahashoval povodne data
	 * 
	 **/
	template<class HashBoxT>
	class HashFirstOperandSubtractionResultBox : 
		public BufferedOneOutputBox<BinaryOperationResultIndexType> {
	public:
		typedef typename HashBoxT::HTH HTH;

		static const  groupType OUTPUT_GROUP;	
		static const  columnType OUTPUT_COLUMN;
		static const  outType OUT_ARC;
		/// Meno parametra definicie, ktory urcuje stupen paralelizacie.
		static const char* PARALLELISM_FACTOR_PARAM_NAME;
		/// Meno parametra definicie, ktory urcuje usek spracovavanych dat.
		static const char* PARALLELISM_INDEX_PARAM_NAME;
		/// Pociatocny index spracovavanych dat.
		size_t from_;
		/// Koncovy index spracovavanuych dat.
		size_t to_;

		HashFirstOperandSubtractionResultBox(const BoxParamPack& p)
			: BufferedOneOutputBox(p, OUTPUT_GROUP, OUTPUT_COLUMN, OUT_ARC),
			version_(p) {
				int parallelism = 
					getBoxParameterValue(p, PARALLELISM_FACTOR_PARAM_NAME, 1);

				int parallelIndex = 
					getBoxParameterValue(p, PARALLELISM_INDEX_PARAM_NAME, 0);

				const size_t size = HTH::getHashTable()->getSize();
				// Velkost useku dat, ktory treba spracovat.
				size_t chunkSize = size / parallelism;
				// Pociatocny index spracovavanych dat.
				from_ = parallelIndex * chunkSize;
				if (parallelIndex + 1 == parallelism) {
					// Pokial sa jedna o posledny usek spracovania tak potom 
					// az po koniec (teda aj zvysok po deleni).
					to_ = size; 
				}
				else {
					to_ = from_ + chunkSize;
				}
		}


		virtual void initImpl() {
			// Len vystup. Vstup resp. spustenie je riesene pomocou signalov.
			add_group(OUTPUT_GROUP, OUTPUT_COLUMN, 
				get_output_descriptors(OUT_ARC));

			initBufferedOutputs();
			wait4SignalsIn();
		}

		virtual void syncMachEtwas() {
			HTH::HashTable* table = 
				HTH::getHashTable();
			table->setSynchronized(false);

			for (;from_ < to_; ++from_) {
				table->getRow(from_)->foreachExtended([&](HTH::ExtendedHashEntry& entry) {

					assert(entry.getCount() == 1);
					auto inputIndices = entry.plainEntry.value;
					if (version_.isBagVersion()) {
						for(auto it = inputIndices.begin(); 
							it != inputIndices.end(); ++it) {
								// Viem len index do hash tabulky a teda druhy 
								// index je nedefinovany.
								BinaryOperationResultIndex index = 
									{*it, -1}; /** -1 max size_t*/
								addOutputData(index);
						}
					}
					else {
						auto it = inputIndices.begin();
						if (it != inputIndices.end()) {
							BinaryOperationResultIndex index = 
							{*it, -1}; /** -1 max size_t*/
							addOutputData(index);
						}
					}
				});
			}
			// Odoslem data z bufferu.
			sendOutputData();
			// Pokial je buffer prazdny tak koniec. Inak necham box opatovne
			// naplanovat.
			if (isOutputEmpty()) {
				send_poisoned(OUT_ARC);
				sendSignalsOut(true);
				wait4SignalsIn();
			}
		}


	private:
		//StaticHashTableHolderType tableHolder_;
		DbOperationVersion version_;
	};

	template<class HashBoxT>
	const  outType jm4bbx::HashFirstOperandSubtractionResultBox<HashBoxT>::
		OUT_ARC = outType(0);

	template<class HashBoxT>
	const  columnType jm4bbx::HashFirstOperandSubtractionResultBox<HashBoxT>::
		OUTPUT_COLUMN = columnType(0);

	template<class HashBoxT>
	const  groupType jm4bbx::HashFirstOperandSubtractionResultBox<HashBoxT>::
		OUTPUT_GROUP = groupType(0);

	template<class HashBoxT>
	const char* HashFirstOperandSubtractionResultBox<HashBoxT>::
		PARALLELISM_FACTOR_PARAM_NAME = "parallelism";

	template<class HashBoxT>
	const char* HashFirstOperandSubtractionResultBox<HashBoxT>::
		PARALLELISM_INDEX_PARAM_NAME = "parallel_index";

} // namespace jm4bbx

#endif
